package cn.objectspace.webssh.service.impl;

import cn.objectspace.webssh.bean.Ssh;
import cn.objectspace.webssh.controller.RealTimeLogsWebSocket;
import cn.objectspace.webssh.service.IRealTimeLogsService;
import cn.objectspace.webssh.utils.ThreadPoolUtils;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Properties;
import java.util.concurrent.ExecutorService;

/**
 * @author xuejingbao
 * @create 2022-11-18 9:13
 */
@Slf4j
@Service
public class RealTimeLogsServiceImpl implements IRealTimeLogsService {

    @Autowired
    private Ssh ssh;

    private final static Integer PORT = 22;

    private final static ExecutorService executorService = ThreadPoolUtils.getThreadPool(
            10,
            10,
            "ssh-connect"
    );

    @Override
    public void initConnection(RealTimeLogsWebSocket ws) {

        executorService.execute(() -> {
            try {
                JSch jSch = new JSch();
                Properties config = new Properties();
                config.put("StrictHostKeyChecking", "no");
                Session session = jSch.getSession(
                        ssh.getUserName(),
                        ssh.getIp(),
                        PORT
                );
                session.setConfig(config);
                session.setPassword(ssh.getPassword());
                session.connect(30000);
                Channel channel = session.openChannel("shell");
                channel.connect(3000);
                ws.setChannel(channel);

                execute(ws, ssh.getCommand() + "\n");

                //读取终端返回的信息流
                InputStream inputStream = channel.getInputStream();
                try {
                    //循环读取
                    byte[] buffer = new byte[1024];
                    int i = 0;
                    //如果没有数据来，线程会一直阻塞在这个地方等待数据。
                    while ((i = inputStream.read(buffer)) != -1) {
                        executiveResponse(ws, Arrays.copyOfRange(buffer, 0, i));
                    }
                } finally {
                    //断开连接后关闭会话
                    session.disconnect();
                    channel.disconnect();
                    if (inputStream != null) {
                        inputStream.close();
                    }
                }
            } catch (JSchException | IOException e) {
                log.error("ssh连接失败", e);
                closeConnection(ws);
                try {
                    ws.getSession().close();
                } catch (IOException ioException) {
                    log.warn("ssh连接失败,关闭session", e);
                }
            }
        });


    }

    @Override
    public void execute(RealTimeLogsWebSocket ws, String command) {
        try {
            executeToRemote(ws.getChannel(), command);
        } catch (IOException e) {
            log.warn("shell发送消息错误", e);
        }
    }

    @Override
    public void executiveResponse(RealTimeLogsWebSocket ws, byte[] messageByte) {
        try {
            ws.getSession().getBasicRemote().sendText(new String(messageByte, StandardCharsets.UTF_8));
        } catch (IOException e) {
            log.error("ws发送消息错误", e);
        }
    }

    @Override
    public void closeConnection(RealTimeLogsWebSocket ws) {
        if (!ObjectUtils.isEmpty(ws.getChannel())) {
            ws.getChannel().disconnect();
        }
    }

    /**
     * 执行至远程ssh
     *
     * @param channel
     * @param command
     * @throws IOException
     */
    private void executeToRemote(Channel channel, String command) throws IOException {
        if (channel != null) {
            OutputStream outputStream = channel.getOutputStream();
            outputStream.write(command.getBytes());
            outputStream.flush();
        }
    }
}
